package edu.zjut.hjg.saas.biz.service.impl;

import static edu.zjut.hjg.saas.biz.support.AppConstants.COLLECTION_NAME_KINDERGARTEN;
import static edu.zjut.hjg.saas.biz.support.AppConstants.DEFAULT_THEME_NAME;
import static edu.zjut.hjg.saas.biz.support.AppConstants.INDEX_NAME_KINDERGARTEN_ID;

import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.annotation.Transactional;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;

import edu.zjut.hjg.saas.biz.dao.ConsumptionDao;
import edu.zjut.hjg.saas.biz.dao.KindergartenDao;
import edu.zjut.hjg.saas.biz.dao.PriceTypeDao;
import edu.zjut.hjg.saas.biz.dao.SubscribeDao;
import edu.zjut.hjg.saas.biz.dao.TenantDao;
import edu.zjut.hjg.saas.biz.meta.KindergartenMeta;
import edu.zjut.hjg.saas.biz.model.Consumption;
import edu.zjut.hjg.saas.biz.model.Kindergarten;
import edu.zjut.hjg.saas.biz.model.PriceType;
import edu.zjut.hjg.saas.biz.model.Subscribe;
import edu.zjut.hjg.saas.biz.model.Tenant;
import edu.zjut.hjg.saas.biz.service.KindergartenService;
import edu.zjut.hjg.saas.biz.service.exception.ServiceException;
import edu.zjut.hjg.saas.biz.support.AppStatus;
import edu.zjut.hjg.saas.biz.support.ChargeStatus;
import edu.zjut.hjg.saas.biz.support.Paginated;
import edu.zjut.hjg.saas.biz.support.ServiceError;
import edu.zjut.hjg.saas.biz.util.PriceTypeUtil;

public class KindergartenServiceImpl implements KindergartenService {
    private static final Log log = LogFactory.getLog(KindergartenServiceImpl.class);

    private DB               mongoDB;
    private KindergartenDao  kindergartenDao;
    private List<String>     themes;
    private SubscribeDao     subscribeDao;
    private ConsumptionDao   consumptionDao;
    private PriceTypeDao     priceTypeDao;

    private TenantDao        tenantDao;

    public void setTenantDao(TenantDao tenantDao) {
        this.tenantDao = tenantDao;
    }

    public void setMongoDB(DB mongoDB) {
        this.mongoDB = mongoDB;
    }

    public void setKindergartenDao(KindergartenDao kindergartenDao) {
        this.kindergartenDao = kindergartenDao;
    }

    public void setThemes(List<String> themes) {
        this.themes = themes;
    }

    public void setSubscribeDao(SubscribeDao subscribeDao) {
        this.subscribeDao = subscribeDao;
    }

    public void setConsumptionDao(ConsumptionDao consumptionDao) {
        this.consumptionDao = consumptionDao;
    }

    public void setPriceTypeDao(PriceTypeDao priceTypeDao) {
        this.priceTypeDao = priceTypeDao;
    }

    @Override
    @Transactional(rollbackFor = { ServiceException.class })
    public Integer create(Kindergarten kindergarten) throws ServiceException {
        log.info("create Kindergarten: kindergarten=" + kindergarten);
        if (this.siteDomainExist(kindergarten.getSiteDomain())) {
            String message = "createKindergarten error, siteDomain Duplicate: siteDomain="
                    + kindergarten.getSiteDomain();
            log.error(message);
            throw new ServiceException(message, ServiceError.SITE_DOMAIN_DUPLICATE);
        }
        Integer id = kindergartenDao.create(kindergarten);
        DBCollection dbCollection = mongoDB.getCollection(COLLECTION_NAME_KINDERGARTEN);
        // 在id属性上创建唯一索引
        dbCollection.ensureIndex(new BasicDBObject("Id", 1), INDEX_NAME_KINDERGARTEN_ID, true);
        // XXX 创建幼儿园网站元数据
        KindergartenMeta meta = new KindergartenMeta();
        meta.setId(id);
        meta.setTheme(DEFAULT_THEME_NAME);
        dbCollection.insert(meta);
        return id;
    }

    @Override
    public boolean siteDomainExist(String siteDomain) {
        return kindergartenDao.findBySiteDomain(siteDomain) != null;
    }

    @Override
    public Kindergarten findBySiteDomain(String siteDomain) {
        Kindergarten kindergarten = kindergartenDao.findBySiteDomain(siteDomain);
        if (kindergarten != null) {
            setMeta(kindergarten);
        }
        return kindergarten;
    }

    @Override
    public Kindergarten findByTenantId(Integer tenantId) {
        Kindergarten kindergarten = kindergartenDao.findByTenantId(tenantId);
        if (kindergarten != null) {
            setMeta(kindergarten);
        }
        return kindergarten;
    }

    @Override
    public Paginated<Kindergarten> listByStatus(AppStatus status, int pageSize, int page) {
        return kindergartenDao.listByStatus(status, pageSize, page);
    }

    @Override
    public void updateStatus(Integer id, AppStatus status) throws ServiceException {
        Kindergarten kindergarten = kindergartenDao.load(id);
        if (kindergarten == null) {
            throw new ServiceException(ServiceError.APP_ID_NOT_FOUND);
        }
        kindergarten.setStatus(status);
        kindergartenDao.update(kindergarten);
    }

    @Override
    public void update(Kindergarten kindergarten) throws ServiceException {
        Kindergarten other = kindergartenDao.findBySiteDomain(kindergarten.getSiteDomain());
        if (other != null && other.getId().intValue() != kindergarten.getId().intValue()) {
            // 域名重复
            String message = "update Kindergarten error, siteDomain Duplicate: siteDomain="
                    + kindergarten.getSiteDomain();
            log.error(message);
            throw new ServiceException(message, ServiceError.SITE_DOMAIN_DUPLICATE);
        }
        kindergartenDao.update(kindergarten);
    }

    private void setMeta(Kindergarten kindergarten) {
        DBCollection coll = mongoDB.getCollection(COLLECTION_NAME_KINDERGARTEN);
        coll.setObjectClass(KindergartenMeta.class);
        KindergartenMeta meta = (KindergartenMeta) coll.findOne(new BasicDBObject("Id",
                kindergarten.getId()));
        kindergarten.setMeta(meta);
    }

    @Override
    public Paginated<Kindergarten> searchByCityCode(String cityCode, int pageSize, int page) {
        return kindergartenDao.listByCityCodeAndStatus(cityCode, AppStatus.ENABLED, pageSize, page);
    }

    @Override
    public Paginated<Kindergarten> searchByName(String name, int pageSize, int page) {
        return kindergartenDao.listByNameAndStatus(name, AppStatus.ENABLED, pageSize, page);
    }

    @Override
    public Paginated<Kindergarten> searchByProvinceCode(String provinceCode, int pageSize, int page) {
        return kindergartenDao.listByProvinceCodeAndStatus(provinceCode, AppStatus.ENABLED,
                pageSize, page);
    }

    @Override
    public void updateConfig(KindergartenMeta meta) {
        if (!themes.contains(meta.getTheme())) {
            meta.setTheme(themes.get(0));
        }
        DBCollection coll = mongoDB.getCollection(COLLECTION_NAME_KINDERGARTEN);
        coll.update(new BasicDBObject("Id", meta.getId()), meta);
    }

    @Override
    public List<String> getThemes() {
        return this.themes;
    }

    @Override
    @Transactional
    public void disable(Kindergarten app) {
        Subscribe subscribe = subscribeDao.findByKindergartenId(app.getId());
        ChargeStatus chargeStatus = subscribe.getChargeStatus();
        PriceType priceType = subscribe.getPriceType();
        app.setStatus(AppStatus.DISABLED);
        kindergartenDao.update(app);
        switch (chargeStatus) {
            case FREE_TRIAL:
                pricing(0.0, subscribe, "免费试用期内，用户手动停用了网站");
                break;
            case CHARGING:
                double spend = PriceTypeUtil.computeLastSpend(app.getStartDate(), priceType);
                pricing(spend, subscribe, "计费期内，用户手动停用了网站");
                break;
            default:
        }
    }

    private void pricing(double spend, Subscribe subscribe, String description) {
        Tenant tenant = subscribe.getKindergarten().getTenant();
        Consumption consumption = new Consumption();
        consumption.setDescription(description);
        consumption.setSpend(spend);
        consumption.setSubscribe(subscribe);
        consumption.setTenant(tenant);
        consumptionDao.create(consumption);
        tenant.setBalance(tenant.getBalance() - spend);
        tenantDao.update(tenant);
    }

    @Override
    @Transactional(rollbackFor = { ServiceException.class })
    public void enable(Kindergarten app, int priceTypeId, int leaseTerm) throws ServiceException {
        PriceType priceType = priceTypeDao.load(priceTypeId);
        if (priceType == null) {
            String message = "no priceType found: priceTypeId=" + priceTypeId;
            log.error(message);
            throw new RuntimeException(message);
        }
        Tenant tenant = app.getTenant();
        if (priceType.getPrice() > tenant.getBalance()) {
            // 余额不足
            throw new ServiceException(ServiceError.BALANCE_NOT_ENOUGH);
        }
        // 启用状态和租期
        app.setStatus(AppStatus.ENABLED);
        Date startDate = new Date();
        app.setStartDate(startDate);
        // 没有试用期
        app.setEndDate(PriceTypeUtil.getEndDate(priceType, leaseTerm, false));
        kindergartenDao.update(app);

        // 订购关系
        Subscribe subscribe = subscribeDao.findByKindergartenId(app.getId());
        subscribe.setPriceType(priceType);
        subscribe.setKindergarten(app);
        // 计费期
        subscribe.setChargeStatus(ChargeStatus.CHARGING);
        subscribe.setOccurTime(startDate);
        subscribeDao.update(subscribe);
    }
}
